cargo run supports --package argument
authorAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 13 Feb 2017 16:02:27 +0000 (19:02 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 13 Feb 2017 19:46:50 +0000 (22:46 +0300)
closes #3529

src/bin/run.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_run.rs
tests/run.rs

index 79a0a326f1aa3bebc0b49d60c9b08e42224e1214..cd242860c4dcedc949c24f4cabd77906c7d304ee 100644 (file)
@@ -1,3 +1,5 @@
+use std::iter::FromIterator;
+
 use cargo::core::Workspace;
 use cargo::ops::{self, MessageFormat, Packages};
 use cargo::util::{CliResult, CliError, Config, Human};
@@ -7,6 +9,7 @@ use cargo::util::important_paths::{find_root_manifest_for_wd};
 pub struct Options {
     flag_bin: Option<String>,
     flag_example: Option<String>,
+    flag_package: Option<String>,
     flag_jobs: Option<u32>,
     flag_features: Vec<String>,
     flag_all_features: bool,
@@ -30,22 +33,23 @@ Usage:
     cargo run [options] [--] [<args>...]
 
 Options:
-    -h, --help              Print this message
-    --bin NAME              Name of the bin target to run
-    --example NAME          Name of the example target to run
-    -j N, --jobs N          Number of parallel jobs, defaults to # of CPUs
-    --release               Build artifacts in release mode, with optimizations
-    --features FEATURES     Space-separated list of features to also build
-    --all-features          Build all available features
-    --no-default-features   Do not build the `default` feature
-    --target TRIPLE         Build for the target triple
-    --manifest-path PATH    Path to the manifest to execute
-    -v, --verbose ...       Use verbose output (-vv very verbose/build.rs output)
-    -q, --quiet             No output printed to stdout
-    --color WHEN            Coloring: auto, always, never
-    --message-format FMT    Error format: human, json [default: human]
-    --frozen                Require Cargo.lock and cache are up to date
-    --locked                Require Cargo.lock is up to date
+    -h, --help                   Print this message
+    --bin NAME                   Name of the bin target to run
+    --example NAME               Name of the example target to run
+    -p SPEC, --package SPEC      Package with the target to run
+    -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
+    --release                    Build artifacts in release mode, with optimizations
+    --features FEATURES          Space-separated list of features to also build
+    --all-features               Build all available features
+    --no-default-features        Do not build the `default` feature
+    --target TRIPLE              Build for the target triple
+    --manifest-path PATH         Path to the manifest to execute
+    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
+    -q, --quiet                  No output printed to stdout
+    --color WHEN                 Coloring: auto, always, never
+    --message-format FMT         Error format: human, json [default: human]
+    --frozen                     Require Cargo.lock and cache are up to date
+    --locked                     Require Cargo.lock is up to date
 
 If neither `--bin` nor `--example` are given, then if the project only has one
 bin target it will be run. Otherwise `--bin` specifies the bin target to run,
@@ -74,6 +78,9 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
         examples.push(s);
     }
 
+    let packages = Vec::from_iter(options.flag_package.iter().cloned());
+    let spec = Packages::Packages(&packages);
+
     let compile_opts = ops::CompileOptions {
         config: config,
         jobs: options.flag_jobs,
@@ -81,7 +88,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
         features: &options.flag_features,
         all_features: options.flag_all_features,
         no_default_features: options.flag_no_default_features,
-        spec: Packages::Packages(&[]),
+        spec: spec,
         release: options.flag_release,
         mode: ops::CompileMode::Build,
         filter: if examples.is_empty() && bins.is_empty() {
index 1c6e6cbac5ad358283d59ce1185ca15d92cac94f..ee882b7b6f4d1ffdc8b830164c84c8b28445e9e2 100644 (file)
@@ -101,7 +101,7 @@ pub enum MessageFormat {
     Json
 }
 
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum Packages<'a> {
     All,
     Packages(&'a [String]),
@@ -141,7 +141,7 @@ pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions<'a>)
 }
 
 pub fn compile_with_exec<'a>(ws: &Workspace<'a>,
-                             options: &CompileOptions<'a>, 
+                             options: &CompileOptions<'a>,
                              exec: Arc<Executor>)
                              -> CargoResult<ops::Compilation<'a>> {
     for member in ws.members() {
index 1c4c4a485c113ac88dd3c8980fe012455847a49d..170e038852b728a9cbd8118f664341aad682bfbb 100644 (file)
@@ -1,16 +1,28 @@
 use std::path::Path;
 
-use ops::{self, CompileFilter};
-use util::{self, CargoResult, ProcessError};
+use ops::{self, CompileFilter, Packages};
+use util::{self, human, CargoResult, ProcessError};
 use core::Workspace;
 
 pub fn run(ws: &Workspace,
            options: &ops::CompileOptions,
            args: &[String]) -> CargoResult<Option<ProcessError>> {
     let config = ws.config();
-    let root = ws.current()?;
 
-    let mut bins = root.manifest().targets().iter().filter(|a| {
+    let pkg = match options.spec {
+        Packages::All => unreachable!("cargo run supports single package only"),
+        Packages::Packages(xs) => match xs.len() {
+            0 => ws.current()?,
+            1 => ws.members()
+                .find(|pkg| pkg.name() == xs[0])
+                .ok_or_else(|| human(
+                    format!("package `{}` is not a member of the workspace", xs[0])
+                ))?,
+            _ => unreachable!("cargo run supports single package only"),
+        }
+    };
+
+    let mut bins = pkg.manifest().targets().iter().filter(|a| {
         !a.is_lib() && !a.is_custom_build() && match options.filter {
             CompileFilter::Everything => a.is_bin(),
             CompileFilter::Only { .. } => options.filter.matches(a),
@@ -41,6 +53,7 @@ pub fn run(ws: &Workspace,
     }
 
     let compile = ops::compile(ws, options)?;
+    assert_eq!(compile.binaries.len(), 1);
     let exe = &compile.binaries[0];
     let exe = match util::without_prefix(&exe, config.cwd()) {
         Some(path) if path.file_name() == Some(path.as_os_str())
@@ -48,7 +61,7 @@ pub fn run(ws: &Workspace,
         Some(path) => path.to_path_buf(),
         None => exe.to_path_buf(),
     };
-    let mut process = compile.target_process(exe, &root)?;
+    let mut process = compile.target_process(exe, &pkg)?;
     process.args(args).cwd(config.cwd());
 
     config.shell().status("Running", process.to_string())?;
index 36d2b5a28aced89d7de0abaf819d31e8d2403da1..a95dcf01e7508965b51afceb197d5208b6621eae 100644 (file)
@@ -652,3 +652,80 @@ fn fail_no_extra_verbose() {
                        .with_stdout("")
                        .with_stderr(""));
 }
+
+#[test]
+fn run_multiple_packages() {
+    let p = project("foo")
+        .file("foo/Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [workspace]
+
+            [dependencies]
+            d1 = { path = "d1" }
+            d2 = { path = "d2" }
+            d3 = { path = "../d3" } # outside of the workspace
+
+            [[bin]]
+            name = "foo"
+        "#)
+        .file("foo/src/foo.rs", "fn main() { println!(\"foo\"); }")
+        .file("foo/d1/Cargo.toml", r#"
+            [package]
+            name = "d1"
+            version = "0.0.1"
+            authors = []
+
+            [[bin]]
+            name = "d1"
+        "#)
+        .file("foo/d1/src/lib.rs", "")
+        .file("foo/d1/src/main.rs", "fn main() { println!(\"d1\"); }")
+        .file("foo/d2/Cargo.toml", r#"
+            [package]
+            name = "d2"
+            version = "0.0.1"
+            authors = []
+
+            [[bin]]
+            name = "d2"
+        "#)
+        .file("foo/d2/src/main.rs", "fn main() { println!(\"d2\"); }")
+        .file("d3/Cargo.toml", r#"
+            [package]
+            name = "d3"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("d3/src/main.rs", "fn main() { println!(\"d2\"); }");
+
+    let p = p.build();
+
+    let cargo = || {
+        let mut process_builder = p.cargo("run");
+        process_builder.cwd(p.root().join("foo"));
+        process_builder
+    };
+
+    assert_that(cargo().arg("-p").arg("d1"),
+                execs().with_status(0).with_stdout("d1"));
+
+    assert_that(cargo().arg("-p").arg("d2").arg("--bin").arg("d2"),
+                execs().with_status(0).with_stdout("d2"));
+
+    assert_that(cargo(),
+                execs().with_status(0).with_stdout("foo"));
+
+    assert_that(cargo().arg("-p").arg("d1").arg("-p").arg("d2"),
+                execs()
+                    .with_status(1)
+                    .with_stderr_contains("[ERROR] Invalid arguments."));
+
+    assert_that(cargo().arg("-p").arg("d3"),
+                execs()
+                    .with_status(101)
+                    .with_stderr_contains("[ERROR] package `d3` is not a member of the workspace"));
+}